package com.helloscala.web
package snippet

import scala.xml.{Text, Unparsed, NodeSeq}

import net.liftweb.http.{SHtml, S, DispatchSnippet}
import net.liftweb.common.{Empty, Loggable, Failure, Full}
import net.liftweb.util.Helpers._
import net.liftweb.util.ClearNodes
import net.liftweb.http.js.{JE, JsCmds, JsCmd}

import com.helloscala.model.MUser
import com.helloscala.helper.HelloHelpers
import com.helloscala.common.{DataDl, DataLink}

object SessionSnippet extends DispatchSnippet with Loggable {

  def dispatch = {
    case "html5Shiv" => html5Shiv
    case "respond" => respond
    case "navbarTop" => navbarTop
    case "mainMenu" => mainMenu
    case "footer" => footer
    case "signIn" => signIn
    case "signUp" => signUp
    case "page404" => page404
    case "page500" => page500
    case "forgotPassword" => forgotPassword
  }

  def signIn(nodeSeq: NodeSeq): NodeSeq = {
    for (account <- W.theAccount.is) {
      logger.debug(s"用户已经登录：${account.id}")
      S.redirectTo(S.param("goto_page") openOr W.href("/index"))
    }

    var id = ""
    var pwd = ""
    val gotoPage = S.param("goto_page") openOr W.href("/index")

    def submitFunc: JsCmd =
      MUser.findById(id, pwd) match {
        case Full(user) =>
          W.saveSessionAndCookie(user)
          JsCmds.Alert("登录成功。") &
            JsCmds.RedirectTo(gotoPage)

        case Failure(msg, e, _) =>
          JE.Call("formControlError", "#sign-in-control-submit", msg).cmd

        case _ =>
          JE.Call("formControlError", "#sign-in-control-submit", "登录失败，请检查账号或密码是否正确！").cmd
      }


    val cssSel =
      "@account" #> SHtml.text(id, id = _) &
        "@password" #> SHtml.password(pwd, pwd = _) &
        "@submit" #> SHtml.hidden(() => submitFunc)

    cssSel(nodeSeq)
  }

  def signUp(nodeSeq: NodeSeq): NodeSeq = {
    W.theAccount.is foreach (_ => S.redirectTo(W.href(S.param("goto_page") openOr "/index")))

    var id = ""
    var pwd = ""
    var pwd2 = ""
    var email = ""
    var first = true

    S.appendJs(JE.Call("signUpPwd", "#sign-up-control-pwd", "#sign-up-control-pwd2").cmd)

    def funcId(v: String): JsCmd =
      if (!MUser.allowedId(v))
        JE.Call("formControlError", "#sign-up-control-id", "账号不符合要求。只能使用英文小写字符、数字、'-'和'_'，且字符数为[2, 32]范围，首字符不能使用数字。").cmd
      else if (MUser.haveId(v))
        JE.Call("formControlError", "#sign-up-control-id", "账号已存在！").cmd
      else
        JE.Call("formControlSuccess", "#sign-up-control-id").cmd

    def funcEmail(v: String): JsCmd =
      if (!MUser.allowedEmail(v))
        JE.Call("formControlError", "#sign-up-control-email", "邮箱地址不符合要求。").cmd
      else if (MUser.haveEmail(v))
        JE.Call("formControlError", "#sign-up-control-email", "邮箱地址已被使用。").cmd
      else
        JE.Call("formControlSuccess", "#sign-up-control-email").cmd

    def funcPwd(v: String): JsCmd =
      if (MUser.allowedPassword(v)) {
        pwd = v
        val cmd = JE.Call("formControlSuccess", "#sign-up-control-pwd").cmd
        if (first) {
          first = false
          cmd
        } else if (v != pwd2)
          JE.Call("formControlError", "#sign-up-control-pwd", "两次密码不匹配！").cmd
        else
          cmd & JE.Call("formControlSuccess", "#sign-up-control-pwd2").cmd
      } else
        JE.Call("formControlError", "#sign-up-control-pwd", "密码不符合要求！").cmd

    def funcPwd2(v: String): JsCmd =
      if (v != pwd)
        JE.Call("formControlError", "#sign-up-control-pwd2", "两次密码不匹配！").cmd
      else if (MUser.allowedPassword(v)) {
        pwd2 = v
        JE.Call("formControlSuccess", "#sign-up-control-pwd").cmd &
          JE.Call("formControlSuccess", "#sign-up-control-pwd2").cmd
      } else
        JE.Call("formControlError", "#sign-up-control-pwd2", "密码不符合要求！").cmd

    def submitFunc: JsCmd =
      if (pwd != pwd2)
        JE.Call("#formControlError", "#sign-up-control-pwd2", "两次密码不匹配！").cmd
      else
        MUser.insert(id, email, pwd) match {
          case Full(u) =>
            W.saveSessionAndCookie(u)
            JsCmds.Alert("注册成功。") &
              JsCmds.RedirectTo(W.href(S.param("goto_page") openOr "/index"))

          case Failure(msg, e, _) =>
            val errorMsg = e.map(_.getLocalizedMessage) openOr msg
            JE.Call("#formControlError", "#sign-up-control-submit", errorMsg).cmd

          case _ =>
            JE.Call("#formControlError", "#sign-up-control-submit", "注册失败").cmd
        }

    val cssSel =
      "@account" #> H.ajaxText(id, funcId _)(id = _) &
        "@email" #> H.ajaxText(email, funcEmail _)(email = _) &
        "@password" #> H.ajaxPassword(pwd, funcPwd _)(pwd = _) &
        "@password2" #> H.ajaxPassword(pwd2, funcPwd2 _)(pwd2 = _) &
        "@submit" #> SHtml.hidden(() => submitFunc)

    cssSel(nodeSeq)
  }

  def page404(nodeSeq: NodeSeq): NodeSeq = {
    val title = S.attr("title") or S.param("title") map (s => new String(base64Decode(s))) openOr "页面未找到"
    val content = S.attr("content") or S.param("content") map (s => new String(base64Decode(s))) openOr ""

    val cssSel =
      "@title *" #> title &
        "@content *" #> content

    cssSel(nodeSeq)
  }

  def page500(nodeSeq: NodeSeq): NodeSeq = {
    val title = S.attr("title") or S.param("title") map (s => new String(base64Decode(s))) openOr "内部错误"
    val content = S.attr("content") or S.param("content") map (s => new String(base64Decode(s))) openOr ""

    val cssSel =
      "@title *" #> title &
        "@content *" #> content

    cssSel(nodeSeq)
  }

  def navbarTop(nodeSeq: NodeSeq): NodeSeq = {
    val navbarClass = S.attr("class").openOr("")
    val config = HelloHelpers.config

    val cssSel =
      ".navbar [class+]" #> navbarClass &
        "#site-logo" #> (
          "img [src]" #> config.site.logo &
            "#site-logo [title]" #> config.description
          ) &
        "href=/topic [href]" #> W.href("/topic") &
        "href=/tag [href]" #> W.href("/tag") &
        "href=/p/user/ [href]" #> W.href("/p/user/") &
        "href=/about [href]" #> W.href("/about")

    cssSel(nodeSeq)
  }

  def mainMenu(nodeSeq: NodeSeq): NodeSeq = {
    val mainMenuSel = W.theAccount.is match {
      case Full(account) =>
        "@account *" #> account.user.nick.getOrElse(account.id) &
          "@account [title]" #> account.id &
          "@account-index" #> ("a [href]" #> W.hrefUser(account.id)) &
          "@not-logged-in" #> ClearNodes

      case _ =>
        val signUpSel =
          if (HelloHelpers.config.allowed_register) "href=/c/sign_up [href]" #> W.hrefGotoPage("/c/sign_up")
          else "href=/c/sign_up" #> ClearNodes

        "@logged" #> ClearNodes &
          "@not-logged-in" #> (
            signUpSel &
              "href=/c/sign_in [href]" #> W.hrefGotoPage("/c/sign_in")
            )
    }

    val cssSel =
      "#main-menu" #> mainMenuSel

    cssSel(nodeSeq)
  }

  def forgotPassword(nodeSeq: NodeSeq): NodeSeq = {
    var email = ""

    def funcEmail(v: String): JsCmd = {
      if (!MUser.allowedEmail(email))
        $("#forgot-passwor-actions :submit").disabled().cmd &
          JE.Call("formControlError", "#forgot-password-email", s"邮件：[${v}] 不符合要求。").cmd
      else if (MUser.size(email = Some(v)) == 1L) {
        email = v
        $("#forgot-passwor-actions :submit").removeDisabled().cmd &
          JE.Call("formControlSuccess", "#forgot-password-email").cmd
      } else
        $("#forgot-passwor-actions :submit").disabled().cmd &
          JE.Call("formControlError", "#forgot-password-email", s"邮件：[${v}] 未被任何用户注册。").cmd
    }

    def funcSubmit(): JsCmd =
      MUser.findById(email) match {
        case Full(user) =>
          val msg = s"已向邮件：[$email] 发送重设密码链接，请登陆邮箱重新设置密码。"
          JE.Call("formControlSuccess", "#forgot-password-email", msg).cmd

        case _ =>
          $("#forgot-passwor-actions :submit").disabled().cmd &
            JE.Call("formControlError", "#forgot-password-email", s"邮件：[$email] 未被任何用户注册。").cmd
      }

    val cssSel =
      "@email" #> SHtml.text(email, funcEmail) &
        "@submit" #> SHtml.hidden(funcSubmit)

    cssSel(nodeSeq)
  }

  def footer(nodeSeq: NodeSeq): NodeSeq = {
    val f = HelloHelpers.config.footer

    val cssSel =
      ".data-link" #> f.links.map(dl =>
        "dt *" #> dl.title.map(Text(_)).getOrElse(Unparsed("&nbsp;")) &
          "dd" #> dl.datas.map {
            data =>
              val content: NodeSeq =
                data.name.map(Text(_)).getOrElse(NodeSeq.Empty) ++
                  <a target="_blank" href={data.url}>
                    {data.content}
                  </a>

              "dd *" #> content
          }) &
        ".data-description *" #> f.description

    cssSel(nodeSeq)
  }

  def html5Shiv(nodeSeq: NodeSeq): NodeSeq =
    Unparsed( """<!--[if lt IE 9]><script src="/asserts/js/html5shiv.js"></script><![endif]-->""")

  def respond(nodeSeq: NodeSeq): NodeSeq =
    Unparsed( """<!--[if lt IE 9]><script src="/asserts/js/respond/respond.min.js"></script><![endif]-->""")

}
